/***************************************************************************
 * Open Object Oriented ANSI C                                             *
 ***************************************************************************
 * Code given under the terms of GNU General Public Licence (GPLv3)        *
 * and without any waranties of any kind including but not limitative to   *
 * waranties on merchandability or any particular applications fitness.    *
 * For more information on GNU/GPL go on :                                 *
 * "http://www.gnu.org/copyleft/gpl.html"                                  *
 *                                                                         *
 * You are allowed to use it for any purposes in the terms of GNU/GPLv3    *
 * (or more).                                                              *
 * This text MUST stay in state on copyed sources (or sources using it)    *
 * and must not be altered.                                                *
 * You are allowed to add your name at the end of the Copylefs list.       *
 ***************************************************************************
 * Copylefts List :                                                        *
 ***************************************************************************
 * 2010-03-20 Patrick PIGNOL <pignol_patrick@hotmail.com> (Original)       *
 * (Type your name here if needed)                                         *
 ***************************************************************************/
#include <string.h>
#include <signal.h>
#include "Mem.h"
#include "String.h"
#include "ExceptionManager.h"

static StringVirtualTable* StringVirtualTableInstance = 0;

String* StringNew(unsigned long int pArgsCount, va_list* pParameters)
{
    String* this;
    this = (String*) malloc(sizeof(String));
    if(!this)
    {
        raise(SIGABRT);
    }
    this->VirtualTable = StringInitialize(this, pArgsCount, pParameters);
    this->Constructor = StringConstructor;
    this->Destructor = StringDestructor;
    this->Clone = StringClone;
    this->Length = StringLength;
    this->CharPointer = StringCharPointer;
    this->Constructor(this, pArgsCount, pParameters);
    return this;
}

StringVirtualTable* StringInitialize(String* this, unsigned long int pArgsCount, va_list* pParameters)
{
    if(!StringVirtualTableInstance)
    {
        StringVirtualTableInstance = malloc(sizeof(StringVirtualTable));
        if(!StringVirtualTableInstance)
        {
            raise(SIGABRT);
        }
        StringVirtualTableInstance->Interface = 0;

        StringVirtualTableInstance->TypeID = STRING_TYPEID;

        StringVirtualTableInstance->AncestorTable = ClassInitialize((Class*)this, pArgsCount, pParameters);

        StringVirtualTableInstance->ReferenceCount = 0;

        StringVirtualTableInstance->new = StringNew;
        StringVirtualTableInstance->Initialize = StringInitialize;
        StringVirtualTableInstance->Garbage = (void(*)(String*))StringVirtualTableInstance->AncestorTable->Garbage;
        StringVirtualTableInstance->TypeOf = (unsigned long int (*)(String*))StringVirtualTableInstance->AncestorTable->TypeOf;
        StringVirtualTableInstance->IsSameTypeThanTypeID = (bool (*)(String*, unsigned long int))StringVirtualTableInstance->AncestorTable->IsSameTypeThanTypeID;
        StringVirtualTableInstance->IsKindOfTypeID = (bool (*)(String* this, unsigned long int))StringVirtualTableInstance->AncestorTable->IsKindOfTypeID;
        StringVirtualTableInstance->IsSameTypeThan = (bool (*)(String* this, Class*))StringVirtualTableInstance->AncestorTable->IsSameTypeThan;
        StringVirtualTableInstance->IsKindOf = (bool (*)(String* this, Class*))StringVirtualTableInstance->AncestorTable->IsKindOf;
    }
    StringVirtualTableInstance->ReferenceCount++;
    return StringVirtualTableInstance;
}

void StringConstructor(String* this, unsigned long int pArgsCount, va_list* pParameters)
{
    unsigned long int vTempLength = 0;
    unsigned long int vTempFound = 0;
    char* vTempString = 0;
    if(pArgsCount > 0)
    {
        vTempString = va_arg(*pParameters, char*);
    }
    else
    {
        vTempString = "";
    }
    vTempFound = (unsigned long int)strchr(vTempString, 0);
    vTempLength =  vTempFound - (unsigned long int)vTempString;
    this->aString = malloc(vTempLength + 1);
    if(!this->aString)
    {
        vTempLength = 0;
        this->aString = malloc(vTempLength + 1);
    }
    else
    {
        memcpy(this->aString, vTempString, vTempLength);
    }
    this->aString[vTempLength] = 0;
    this->aLength = vTempLength;
}

void StringDestructor(String* this)
{
    if(this->aString)
    {
        this->aLength = 0;
        free(this->aString);
        this->aString = 0;
    }
}

String* StringClone(String* this)
{
    String* vClone = 0;
    char* vTemp = 0;
    vTemp = malloc(this->aLength + 1);
    memcpy(vTemp, this->aString, this->aLength);
    vTemp[this->aLength] = 0;
    vClone = (String*) new((void* (*)(unsigned long int, va_list*))StringNew, 1, vTemp);
    return vClone;
}

unsigned long int StringLength(String* this)
{
    return this->aLength;
}

const char* const StringCharPointer(String* this, const char* pString)
{
    if(pString)
    {
        unsigned long int vTempLength = 0;
        unsigned long int vTempFound = 0;
        vTempFound = (unsigned long int)strchr(pString, 0);
        vTempLength =  vTempFound - (unsigned long int)pString;
        if(this->aString)
        {
            free(this->aString);
            this->aString = 0;
        }
        this->aString = malloc(vTempLength + 1);
        if(!this->aString)
        {
            raise(SIGABRT);
        }
        memcpy(this->aString, pString, vTempLength);
        this->aString[vTempLength] = 0;
        this->aLength = vTempLength;
    }
    return this->aString;
}

void StringAppendString(String* this, const String* pString)
{
    unsigned long int vTempFound = 0;
    unsigned long int vTempLength = 0;
    vTempFound = pString->Length((String*)pString);
    vTempLength = this->aLength + vTempFound;
    this->aString = realloc(this->aString, vTempLength + 1);
    if(!this->aString)
    {
        raise(SIGABRT);
    }
    memcpy(&this->aString[this->aLength], (char*)pString->CharPointer((String*)pString, (char*)0), vTempFound);
    this->aString[vTempLength] = 0;
    this->aLength = vTempLength;
}

void StringPrependString(String* this, const String* pString)
{
    unsigned long int vTempFound = 0;
    unsigned long int vTempLength = 0;
    vTempFound = pString->Length((String*)pString);
    vTempLength = this->aLength + vTempFound;
    this->aString = realloc(this->aString, vTempLength + 1);
    if(!this->aString)
    {
        raise(SIGABRT);
    }
    memmove(&this->aString[vTempFound], this->aString, this->aLength);
    memcpy(this->aString, pString->CharPointer((String*)pString, (char*)0), vTempFound);
    this->aString[vTempLength] = 0;
    this->aLength = vTempLength;
}

bool StringFindChar(String* this, unsigned long int* pIndex, char pChar, StringDirection pDirection)
{
    unsigned long int vIndex;
    if(pDirection == StringDirectionBackward)
    {
        vIndex = this->aLength - 1;
    }
    else
    {
        vIndex = 0;
    }
    return this->FindCharNext(this, pIndex, pChar, vIndex, pDirection);
}

bool StringFindCharNext(String* this, unsigned long int* pIndex, char pChar, unsigned long int pStarIndex, StringDirection pDirection)
{
    unsigned long int vEndSearch;
    char* vTempString = this->aString;
    if(pDirection == StringDirectionBackward)
    {
        vEndSearch = 0;
    }
    else
    {
        vEndSearch = this->aLength;
    }
    while(!(pStarIndex == vEndSearch))
    {
        if(vTempString[pStarIndex] == pChar)
        {
            *pIndex = pStarIndex;
            return true;
        }
        pStarIndex += (unsigned long int) pDirection;
    }
    return false;
}

bool StringFindString(String* this, unsigned long int* pIndex, const String* pSubString, StringDirection pDirection)
{
    unsigned long int vIndex;
    if(pDirection == StringDirectionBackward)
    {
        vIndex = 0;
    }
    else
    {
        vIndex = this->aLength - 1;
    }
    return this->FindStringNext(this, pIndex, vIndex, pSubString, pDirection);
}

bool StringFindStringNext(String* this, unsigned long int* pIndex, unsigned long int pStartIndex, const String* pSubString, StringDirection pDirection)
{
    unsigned long int vEndSearch;
    unsigned long int vSubStringLength;
    char* vTempString;

    if(pSubString->Length((String*)pSubString)>this->aLength)
    {
        ((ExceptionManager*)new((void*(*)(unsigned long int, va_list*))ExceptionManagerNew, 0))->throw(new((void*(*)(unsigned long int, va_list*))ExceptionNew, 1, "ExceptionStringSubString :\n\tpSubString parameter is longer than the String itself."));
    }
    vSubStringLength = pSubString->Length((String*)pSubString);
    if((pStartIndex + vSubStringLength) > this->aLength)
    {
        pStartIndex = this->aLength - vSubStringLength;
    }
    vTempString = (char*)pSubString->CharPointer((String*)pSubString, (char*)0);
    if(pDirection == StringDirectionBackward)
    {
        vEndSearch = 0;
    }
    else
    {
        vEndSearch = this->aLength;
    }
    while(!(pStartIndex == vEndSearch))
    {
        if(vTempString[pStartIndex] == vTempString[0])
        {
            if(!(memcmp(&vTempString[pStartIndex], vTempString, vSubStringLength)))
            {
                pIndex = &pStartIndex;
                return true;
            }
        }
        pStartIndex += (unsigned long int) pDirection;
    }
    return false;
}

bool StringIsEmptyOfChar(String* this, char pChar)
{
    unsigned long int vIndex;
    return !StringFindChar(this, &vIndex, pChar, StringDirectionForward);
}

bool StringIsEmptyOfString(String* this, String* pString)
{
    unsigned long int vIndex;
    return !StringFindString(this, &vIndex, pString, StringDirectionForward);
}

char StringSubChar(String* this, unsigned long int pIndex)
{
    return this->aString[pIndex];
}

String* StringSubString(String* this, unsigned long int pStartIndex, unsigned long int pLength)
{
    String* vString;
    char* vTempString = 0;
    if(pStartIndex > this->aLength)
    {
        ((ExceptionManager*)new((void* (*)(unsigned long int, va_list*))ExceptionManagerNew, 0, (va_list*)0))->throw((Exception*)new((void*(*)(unsigned long int, va_list*))ExceptionNew, 1, "String:\n\tStartIndexOut of range."));
    }
    if(pStartIndex + pLength > this->aLength)
    {
        pLength = this->aLength - pStartIndex;
    }
    vTempString = malloc(pLength+1);
    if(!vTempString)
    {
        raise(SIGABRT);
    }
    memcpy(vTempString, &this->aString[pStartIndex], pLength);
    vTempString[pLength] = 0;
    vString = new((void*(*)(unsigned long int, va_list*))StringNew,1,vTempString);
    free(vTempString);
    return vString;
}

StringCompared StringCompare(String* this, String* pString)
{
    int vResult;
    unsigned long int vLength = pString->Length(pString);
    if(this->aLength > vLength)
    {
        vLength = this->aLength;
    }
    vResult = memcmp(this->aString, pString->CharPointer(pString, (char*)0), vLength);
    if(vResult == 0)
    {
        return StringComparedEqual;
    }
    if(vResult < 0)
    {
        return StringComparedBefore;
    }
    else
    {
        return StringComparedAfter;
    }
}
